package com.fruitstore.jdbc2.dao.base;



import com.bookstore.utils.DruidUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public abstract class BaseDao<T> {

    protected Connection conn ;
    protected PreparedStatement psmt ;
    protected ResultSet rs ;

    // T的Class对象
    private Class entityClass;

    // 无参构造方法
    public BaseDao() {
        // getClass()获取Class对象，当前我们执行的是new FruitDaoImpl(),创建的是FruitDaoImpl的实例
        // 那么子类构造方法内部首先会调用父类（BaseDao）的无参构造方法
        // 因此geClass获取的是FruitDaoImpl的Class
        // 所以需要调用getGenericSuperclass来获取BaseDao的Class
        Type genericType = getClass().getGenericSuperclass();
        // 获取BaseDao范型参数类型
        Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
        // 获取到的泛型<T>中T的真实的类型
        Type actualType = actualTypeArguments[0].getClass();
        try {
            entityClass = Class.forName(actualType.getTypeName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 获取数据库连接
    protected Connection getConn(){
        try {
            conn=DruidUtils.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }

    // 执行更新，如果是插入语句返回自增长id的值，如果是更新和删除则返回影响行数
    protected int executeUpdate(String sql, Object... params){
        boolean insertFlag = false;
        insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
        try {
            conn = getConn();
            if(insertFlag){//如果是INSERT语句，则获取自增长id值
                psmt = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
            }else {
                psmt = conn.prepareStatement(sql);
            }
            setParames(psmt, params);
            int count = psmt.executeUpdate();
            rs = psmt.getGeneratedKeys();
            if(rs.next()){
                return ((Long)rs.getLong(1)).intValue();
            }
            return count;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rs,psmt,conn);
        }
        return 0;
    }

    // 执行查询，返回List
    protected List<T> executeQuery(String sql, Object... params){
        List<T> list = new ArrayList<>();
        try {
            conn = getConn();
            psmt = conn.prepareStatement(sql);
            // 设置参数
            setParames(psmt, params);
            rs = psmt.executeQuery();
            // 通过rs可以获取结果集的元数据
            // 元数据：描述结果集数据的数据，简单讲，就是这些结果集有哪些列，什么类型等等
            ResultSetMetaData rsmd = rs.getMetaData();
            // 获取结果集的列数
            int columnCount = rsmd.getColumnCount();
            // 6.解析结果
            while (rs.next()){
                T entity = (T) entityClass.newInstance();
                // 循环遍历所有列，并给每一列赋值
                for (int i = 0; i < columnCount; i++) {
                    String columnName = rsmd.getColumnName(i + 1);
                    Object columnValue = rs.getObject(i + 1);
                    setValue(entity, columnName, columnValue);
                }
                // for循环之后，就得到一行的所有列的数据了
                // 把行数据添加到list中
                list.add(entity);
            }
        } catch (SQLException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            close(rs,psmt,conn);
        }
        return list;
    }
    //通过反射技术给obj对象的property属性赋propertyValue值
    private void setValue(Object obj, String propertyName, Object propertyValue) {
        Class clazz = obj.getClass();
        try {
            //获取propertyName字符串应对的属性名，例如：“fid”去找obj对象的fid属性
            Field field = clazz.getDeclaredField(propertyName);
            if(field!=null){
                field.setAccessible(true);
                field.set(obj, propertyValue);
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void setParames(PreparedStatement psmt, Object[] params) throws SQLException {
        if(params!=null && params.length>0){
            for (int i = 0; i < params.length; i++) {
                psmt.setObject(i+1,params[i]);
            }
        }
    }

    // 执行查询，返回单个实体对象
    protected T load(String sql, Object... params){
        try {
            conn = getConn();
            psmt = conn.prepareStatement(sql);
            // 设置参数
            setParames(psmt, params);
            rs = psmt.executeQuery();
            // 通过rs可以获取结果集的元数据
            // 元数据：描述结果集数据的数据，简单讲，就是这些结果集有哪些列，什么类型等等
            ResultSetMetaData rsmd = rs.getMetaData();
            // 获取结果集的列数
            int columnCount = rsmd.getColumnCount();
            // 6.解析结果
            if (rs.next()){
                T entity = (T) entityClass.newInstance();
                // 循环遍历所有列，并给每一列赋值
                for (int i = 0; i < columnCount; i++) {
                    String columnName = rsmd.getColumnName(i + 1);
                    Object columnValue = rs.getObject(i + 1);
                    setValue(entity, columnName, columnValue);
                }
                // for循环之后，就得到一行的所有列的数据了
                return entity;
            }
        } catch (SQLException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            close(rs,psmt,conn);
        }
        return null;
    }

    // 执行复杂查询，返回不是一个实体，例如返回统计结果的情况
    protected Object[] executeComplexQuery(String sql, Object... params){
        try {
            conn = getConn();
            psmt = conn.prepareStatement(sql);
            // 设置参数
            setParames(psmt, params);
            rs = psmt.executeQuery();
            // 通过rs可以获取结果集的元数据
            // 元数据：描述结果集数据的数据，简单讲，就是这些结果集有哪些列，什么类型等等
            ResultSetMetaData rsmd = rs.getMetaData();
            // 获取结果集的列数
            int columnCount = rsmd.getColumnCount();
            Object[] columnValueArr = new Object[columnCount];
            // 6.解析结果
            if (rs.next()){
                // 循环遍历所有列，并给每一列赋值
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    columnValueArr[i]=columnValue;//把 行数据的值放到Object对象
                }
                // for循环之后，就得到一行的所有列的数据了
                return columnValueArr;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rs,psmt,conn);
        }
        return null;
    }

    // 关闭数据库
    protected void close(ResultSet rs, PreparedStatement psmt, Connection conn){
        try {
            if(rs != null){
                rs.close();
            }
            if(psmt!=null){
                psmt.close();
            }
            if(conn!=null && !conn.isClosed()){
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
//            throw new RuntimeException(e);
        }
    }
}
